﻿using System;
using System.Configuration;
using System.IO;

namespace MockingBird.Simulator.Framework
{
    using System.Diagnostics;
    using System.Threading;
    using Common.Infrastructure.DependencyResolution;
    using Common.Infrastructure.Logging;
    using Microsoft.Practices.Unity;
    using MockingBird.Simulator.Framework.Configuration;
    using MockingBird.Simulator.Framework.Exceptions;
    using MockingBird.Simulator.Framework.Interfaces;


    /// <summary>
    /// Implements a factory pattern where it uses a handler resolver and then returns the appropriate handler
    /// </summary>
    public class HandlerDispenser : IHandlerDispenser
    {
        private ILogger logger;
        private IHandlerResolver handlerResolver;
        private IMessageHandler locatedHandler;
        private ApplicationConfiguration simulatorAppConfig;

        /// <summary>
        /// Property to get the name of the handler for the current request
        /// </summary>
        public string CurrentRequestHandlerName { get; private set; }

        public HandlerDispenser(IHandlerResolver resolver, ILogger logger, ApplicationConfiguration targetAppConfig)
        {
            Debug.WriteLine(string.Format("HandlerDispenser.ctor on thread ID: {0}", Thread.CurrentThread.ManagedThreadId));
            this.logger = logger;
            this.handlerResolver = resolver;

            this.simulatorAppConfig = targetAppConfig;
        }

        public IMessageHandler GetHandler(IExecutionContext context)
        {
            //TODO: We may want to parse the endpoint full name here
            //because the standard hasnt been identified fully. 
            //is it only the final segment? what if people have a tcp://customer. and http://customer

            if (handlerResolver == null)
            {
                handlerResolver = DependencyResolver.DefaultContainer.Resolve<IHandlerResolver>();
            }

            try
            {
                CurrentRequestHandlerName = handlerResolver.GetHandlerNameForEndpoint(context.EndpointName);
                this.logger.Log("Current Request Handler Name :" + CurrentRequestHandlerName);

                Type handlerType = handlerResolver.GetHandlerType(CurrentRequestHandlerName);
                if (handlerType == null)
                {
                    throw new ApplicationOperationException("handler resolution returned null type for endpoint" + context.EndpointFullName);
                }
                this.logger.LogInfo("Located a type for the handler. Going to create one");
                this.locatedHandler = this.CreateHandlerInstance(handlerType, CurrentRequestHandlerName);
                this.logger.LogInfo("Created a handler type");

            }
            catch (InvalidDataException idx)
            {
                throw new ApplicationConfigurationException(idx, "could not locate a handler");
            }
            catch (ConfigurationErrorsException iox)
            {
                throw new ApplicationOperationException(iox, "could not locate a handler");
            }
            catch (ApplicationConfigurationException aEx)
            {
                throw;
            }

            //it is not null so return the located handler
            return locatedHandler;
        }

        private IMessageHandler CreateHandlerInstance(Type handlerType, string handlerName)
        {
            //Now create an instance of the handler and return it
            //We rely on the DependencyResolver to pick up the right container and fill up an instance
            //and return it to us

            try
            {
                //DependencyResolver.ConfigureDefaultContainer(handlerName);
                IUnityContainer cc = DependencyResolver.DefaultContainer.CreateChildContainer();
                DependencyResolver.ConfigureContainer(cc, handlerName);
                //DependencyResolver.ConfigureHandlerContainer(handlerName);
                LoggingConfigurationProvider lcp = new LoggingConfigurationProvider();
                lcp.LoggingConfigurationFolder = this.simulatorAppConfig.ConfigurationFilesRoot;
                DependencyResolver.DefaultContainer.RegisterInstance<ILoggingConfigurationProvider>(lcp);

                IMessageHandler handler = cc.Resolve<IMessageHandler>();
                return handler;
            }
            catch (Exception e)
            {
                throw new ApplicationConfigurationException(e, "Could not build handler instance. Ensure that the Unity config file has all the dependencies registered correctly. Inner Exception is :" + e.ToString());
            }
        }
    }
}
